import Vue from 'vue';
import { createTestingPinia } from '@pinia/testing';
import { PiniaVuePlugin } from 'pinia';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import FileTreeBrowser, {
  TREE_WIDTH,
  FILE_TREE_BROWSER_STORAGE_KEY,
} from '~/repository/file_tree_browser/file_tree_browser.vue';
import FileBrowserHeight from '~/diffs/components/file_browser_height.vue';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { useFileTreeBrowserVisibility } from '~/repository/stores/file_tree_browser_visibility';
import { useMainContainer } from '~/pinia/global_stores/main_container';

Vue.use(PiniaVuePlugin);
jest.mock('~/pinia/global_stores/main_container', () => ({ useMainContainer: jest.fn() }));

describe('FileTreeBrowser', () => {
  let wrapper;
  let pinia;
  let fileTreeBrowserStore;
  let mockMainContainerStore;

  useLocalStorageSpy();

  const findFileBrowserHeight = () => wrapper.findComponent(FileBrowserHeight);
  const findPanelResizer = () => wrapper.findComponent(PanelResizer);
  const findOverlay = () => wrapper.findByTestId('overlay');
  const findFeedbackButton = () => wrapper.findByText('Provide feedback');

  afterEach(() => {
    localStorage.clear();
  });

  const createComponent = (routeName = 'blobPathDecoded') => {
    wrapper = shallowMountExtended(FileTreeBrowser, {
      propsData: {
        projectPath: 'group/project',
        currentRef: 'main',
        refType: 'branch',
      },
      mocks: {
        $route: {
          name: routeName,
        },
      },
      pinia,
    });
  };

  describe('when not on project overview page', () => {
    beforeEach(() => {
      mockMainContainerStore = { isWide: true };
      useMainContainer.mockReturnValue(mockMainContainerStore);
      pinia = createTestingPinia({ stubActions: false });
      fileTreeBrowserStore = useFileTreeBrowserVisibility(pinia);
      fileTreeBrowserStore.setFileTreeBrowserIsExpanded(true);
      createComponent();
    });

    it('renders the file browser height component', () => {
      expect(findFileBrowserHeight().exists()).toBe(true);
      expect(findFileBrowserHeight().attributes('style')).toBe(`--tree-width: ${TREE_WIDTH}px;`);
    });

    describe('file tree browser overlay', () => {
      it('does not show the overlay when peek is not on', () => {
        expect(findOverlay().exists()).toBe(false);
      });

      it('renders the overlay when peek is on', () => {
        fileTreeBrowserStore.setFileTreeBrowserIsPeekOn(true);
        createComponent();
        expect(findOverlay().exists()).toBe(true);
        expect(findOverlay().isVisible()).toBe(true);
      });
    });

    describe('FileBrowserHeight v-show visibility', () => {
      it.each`
        isExpanded | isPeekOn | shouldBeVisible
        ${false}   | ${false} | ${false}
        ${true}    | ${false} | ${true}
        ${false}   | ${true}  | ${true}
      `(
        'file tree browser visibility is $shouldBeVisible when expanded=$isExpanded and peekOn=$isPeekOn',
        ({ isExpanded, isPeekOn, shouldBeVisible }) => {
          fileTreeBrowserStore.setFileTreeBrowserIsExpanded(isExpanded);
          fileTreeBrowserStore.setFileTreeBrowserIsPeekOn(isPeekOn);

          createComponent();

          expect(findFileBrowserHeight().isVisible()).toBe(shouldBeVisible);
        },
      );
    });

    describe('visibilityClasses', () => {
      it.each`
        isExpanded | isPeekOn | expectedClasses
        ${false}   | ${false} | ${''}
        ${true}    | ${false} | ${'file-tree-browser-expanded'}
        ${false}   | ${true}  | ${'file-tree-browser-peek'}
      `(
        'returns correct classes when expanded=$isExpanded and peekOn=$isPeekOn',
        ({ isExpanded, isPeekOn, expectedClasses }) => {
          fileTreeBrowserStore.setFileTreeBrowserIsExpanded(isExpanded);
          fileTreeBrowserStore.setFileTreeBrowserIsPeekOn(isPeekOn);

          createComponent();

          const hasClassName = Object.keys(wrapper.vm.visibilityClasses).some((key) =>
            key.includes(expectedClasses),
          );

          expect(hasClassName).toBe(true);
        },
      );
    });

    describe('PanelResizer component', () => {
      it('renders the panel resizer component', () => {
        expect(findPanelResizer().exists()).toBe(true);
      });

      it('updates tree width when panel resizer emits update:size', async () => {
        const newWidth = 400;

        await findPanelResizer().vm.$emit('update:size', newWidth);

        expect(findFileBrowserHeight().attributes('style')).toBe(`--tree-width: ${newWidth}px;`);
      });

      it('saves tree width preference when panel resizer emits resize-end', async () => {
        const newWidth = 400;

        await findPanelResizer().vm.$emit('resize-end', newWidth);

        expect(localStorage.setItem).toHaveBeenCalledWith(FILE_TREE_BROWSER_STORAGE_KEY, newWidth);
        expect(findFileBrowserHeight().attributes('style')).toBe(`--tree-width: ${newWidth}px;`);
      });
    });

    describe('localStorage handling', () => {
      it('restores tree width from localStorage on component creation', () => {
        const storedWidth = 350;
        localStorage.setItem(FILE_TREE_BROWSER_STORAGE_KEY, storedWidth.toString());

        createComponent();

        expect(localStorage.getItem).toHaveBeenCalledWith(FILE_TREE_BROWSER_STORAGE_KEY);
        expect(findFileBrowserHeight().attributes('style')).toBe(`--tree-width: ${storedWidth}px;`);
      });

      it('uses default width when localStorage is empty', () => {
        createComponent();

        expect(findFileBrowserHeight().attributes('style')).toBe(`--tree-width: ${TREE_WIDTH}px;`);
      });
    });

    it('renders a provide feedback button', () => {
      const feedbackButton = findFeedbackButton();

      expect(feedbackButton.attributes('target')).toBe('_blank');
      expect(feedbackButton.attributes('icon')).toBe('comment-dots');
      expect(feedbackButton.attributes('rel')).toBe('noopener noreferrer');
      expect(feedbackButton.attributes('href')).toBe(
        'https://gitlab.com/gitlab-org/gitlab/-/issues/581271',
      );
    });

    describe('enableStickyHeight prop', () => {
      it.each`
        isWide   | expectedEnableStickyHeight | description
        ${true}  | ${true}                    | ${'passes enableStickyHeight as true when isWide is true'}
        ${false} | ${false}                   | ${'passes enableStickyHeight as false when isWide is false'}
      `('$description', ({ isWide, expectedEnableStickyHeight }) => {
        mockMainContainerStore.isWide = isWide;
        pinia = createTestingPinia({ stubActions: false });
        fileTreeBrowserStore = useFileTreeBrowserVisibility(pinia);
        createComponent();

        expect(findFileBrowserHeight().props('enableStickyHeight')).toBe(
          expectedEnableStickyHeight,
        );
      });
    });
  });
});
